package org.jgs1904.spring.demo03;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @author: 默苍璃
 * @date: 2022-01-03 16:59
 * <p>
 * JDK代理类
 * <p>
 * JDK 动态代理类必须实现反射包中的 java.lang.reflect.InvocationHandler 接口
 * 这个实现类用来 自动生成 代理类  也就是前面的 Proxy 和 UserServiceProxy
 * 实现了InvocationHandler
 */
public class ProxyInvocationHandler implements InvocationHandler {


    /**
     * 抽象接口
     */
    private Rent rent;

    public void setRent(Rent rent) {
        this.rent = rent;
    }


    /**
     * 生成得到代理类
     * 获取代理类的方法getProxy()
     *
     * @return
     */
    public Object getProxy() {
        /**
         *
         * 作用是： 创建代理对象， 等同于静态代理中的
         * Proxy proxy = new Proxy(host);
         * UserServiceProxy proxy = new UserServiceProxy();         *
         *
         * 创建 JDK 动态代理类实例同样也是使用反射包中的 java.lang.reflect.Proxy 类进行创建
         *
         * 通过Proxy类的静态方法newProxyInstance方法去生成一个代理类，这个静态方法接收三个参数，分别是
         *
         *目标类的类加载器
         *目标类实现的接口集合
         *InvocationHandler实例(我们自己写的，代理类要完成的功能)
         *
         *
         * Proxy的newProxyInstance方法先通过传递的参数类加载器和类所实现的所有的接口，
         * 先从缓存获取代理类,
         * 如果没有再去生成一个代理类，然后进行一些安全校验，
         * 如果接口访问标识都是public的话，那生成的代理类都放到默认包下：com.sum.proxy；
         *
         * 然后通过通过代理类获取类的构造方法，进行setAccessible(),
         * setAccessible()是启用和禁用访问安全检查的开关，可以操控私有的成员方法及属性等。
         */
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
    }


    /**
     * 处理代理实例.并返回结果
     * 重写了invoke()
     *
     * @param proxy  代理目标对象的代理对象，它是真实的代理对象。 (jdk创建的代理对象，无需赋值)
     * @param method 执行目标类的方法   (目标类中的方法，jdk提供method对象的)
     * @param args   执行目标类的方法的参数     (目标类中方法的参数， jdk提供的)
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        // 前置增强
        seeHouse();

        /*
         * 动态代理的本质,就是使用反射机制实现
         * 调用Method的invoke方法，传递两个参数，分别是
         * 所需要代理的目标类对象  rent
         * 参数列表 args
         */
        Object invoke = method.invoke(rent, args);

        // 后置增强
        giveMoney();

        return invoke;
    }


    public void seeHouse() {
        System.out.println("看房子");
    }


    public void giveMoney() {
        System.out.println("给钱");
    }

}
